from datetime import datetime, timedelta, timezone
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordBearer
from apps.users.models import User

# 配置信息
SECRET_KEY = "r3htEXQbaCxTCHJoUH03+P0n6FFKr5qGDiT84HNNxLg="  # 请更改为安全的密钥
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token", scheme_name="jwt")


def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password):
    return pwd_context.hash(password)


async def authenticate_user(username: str, password: str):
    user = await User.get_or_none(username=username)
    if not user or not verify_password(password, user.hashed_password):
        return False
    return user


def create_access_token(data: dict, expires_delta: timedelta = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now() + expires_delta
    else:
        expire = datetime.now() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


async def get_current_user(request: Request) -> User:
    return request.state.user


def public_route(func=None):
    if func is None:
        return public_route

    setattr(func, "is_public", True)
    return func


async def conditional_login_required(request: Request):
    # 检查是否是 OpenAPI 相关路径
    if request.url.path in ["/docs", "/redoc", "/openapi.json"]:
        return
    # 检查是否是公开路由
    if getattr(request.scope.get("route").endpoint, "is_public", False):
        return

    try:
        token = await oauth2_scheme(request)
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401, detail="无效的认证凭证")

        user = await User.get_or_raise(username=username)
        if not user.active:
            raise HTTPException(status_code=400, detail="账户不可用")

        request.state.user = user
    except HTTPException:
        raise
    except Exception:
        raise HTTPException(status_code=401, detail="无效的认证凭证")
